package com.ruoyi.app.business.project;

import cn.hutool.core.collection.CollUtil;
import com.ruoyi.app.business.message.MessageBusiness;
import com.ruoyi.app.config.util.UserUtil;
import com.ruoyi.app.converter.ProjectLaborConverter;
import com.ruoyi.app.domain.message.properties.PropertiesExpend;
import com.ruoyi.app.domain.project.labor.ProjectLaborApply;
import com.ruoyi.app.domain.project.labor.ProjectLaborFire;
import com.ruoyi.app.domain.project.labor.ProjectLaborHire;
import com.ruoyi.app.domain.project.labor.ProjectLaborInfo;
import com.ruoyi.common.core.enums.YesNoEnum;
import com.ruoyi.common.core.utils.CommonUtils;
import com.ruoyi.common.core.utils.LocalDateUtils;
import com.ruoyi.common.core.utils.PageUtils;
import com.ruoyi.common.core.web.page.CommonPageInfo;
import com.ruoyi.service.company.service.CompanyService;
import com.ruoyi.service.message.enums.MessageTemplateEnum;
import com.ruoyi.service.project.entity.ProjectEntity;
import com.ruoyi.service.project.entity.ProjectLaborEntity;
import com.ruoyi.service.project.enums.HireStatusEnum;
import com.ruoyi.service.project.enums.ProjectStatusEnum;
import com.ruoyi.service.project.model.PostInfo;
import com.ruoyi.service.project.query.ProjectLaborBaseQuery;
import com.ruoyi.service.project.service.ProjectLaborService;
import com.ruoyi.service.project.service.ProjectService;
import com.ruoyi.service.schedule.service.UserScheduleService;
import com.ruoyi.service.user.entity.UserEntity;
import com.ruoyi.service.user.model.UserInfoModel;
import com.ruoyi.service.user.service.UserService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * <p>
 * 作用：项目相关业务
 * </p>
 *
 * @author Zane
 * @since 2025-10-10 2:19
 */
@Slf4j
@Component
public class ProjectLaborBusiness {

  @Resource
  private ProjectLaborService projectLaborService;
  @Resource
  private ProjectService projectService;
  @Resource
  private CompanyService companyService;

  @Resource
  private UserService userService;
  @Resource
  private UserScheduleService userScheduleService;
  @Resource
  private MessageBusiness messageBusiness;
  @Resource
  private TransactionTemplate transactionTemplate;


  public CommonPageInfo<ProjectLaborInfo> listHired(ProjectLaborBaseQuery laborQuery) {
    CommonPageInfo<ProjectLaborEntity> pageInfo = PageUtils.startPage(laborQuery.getPageNum(),
        laborQuery.getPageSize());
    pageInfo.initData(
        Optional.ofNullable(projectLaborService.getHiredByProjectId(laborQuery.getProjectId()))
            .orElseGet(ArrayList::new));
    return buildResult(pageInfo, laborQuery.getProjectId());
  }

  public CommonPageInfo<ProjectLaborInfo> listCouldHired(ProjectLaborBaseQuery laborQuery) {
    final ProjectEntity project = CommonUtils.requireNotNull(
        projectService.getById(laborQuery.getProjectId()), "项目ID不存在");
    CommonUtils.assertsTrueWithMessage(
        UserUtil.getUserId().compareTo(project.getProjectLeader()) == 0,
        "您不具备该项目人员管理权限,请联系管理员");
    CommonPageInfo<ProjectLaborEntity> pageInfo = PageUtils.startPage(laborQuery.getPageNum(),
        laborQuery.getPageSize());
    pageInfo.initData(
        Optional.ofNullable(projectLaborService.couldHiredByProjectId(laborQuery.getProjectId()))
            .orElseGet(ArrayList::new));
    return buildResult(pageInfo, laborQuery.getProjectId());
  }

  public void apply(ProjectLaborApply laborApply) {
    final ProjectEntity project = CommonUtils.requireNotNull(
        projectService.getById(laborApply.getProjectId()), "项目ID不存在");
    //判断岗位是否招满
    final long count = project.getPostsInfoList().stream()
        .filter(v -> v.getPostCode().equals(laborApply.getPostCode())
            && YesNoEnum.YES.eqCode(v.getIsHiring())).count();
    if (count == 0) {
      throw new RuntimeException("岗位已招满");
    }
    final UserInfoModel loginUser = UserUtil.getLoginUser();
    final LocalDateTime now = LocalDateTime.now();
    ProjectLaborEntity productLabor = new ProjectLaborEntity()
        .setProjectId(laborApply.getProjectId())
        .setLaborId(loginUser.getUserId())
        .setLaborName(loginUser.getName())
        .setLaborPhone(loginUser.getPhone())
        .setPostCode(laborApply.getPostCode())
        .setPostName(laborApply.getPostName())
        .setInvitedFlag(YesNoEnum.NO.getCode())
        .setCreateBy(loginUser.getUserId())
        .setCreateTime(now)
        .setUpdateBy(loginUser.getUserId())
        .setUpdateTime(now);
    projectLaborService.save(productLabor);
  }

  public void hire(ProjectLaborHire laborHire) {
    final ProjectEntity project = CommonUtils.requireNotNull(
        projectService.getById(laborHire.getProjectId()), "项目ID不存在");
    //判断登录用户权限
    final Long userId = UserUtil.getUserId();
    CommonUtils.assertsTrueWithMessage(userId.compareTo(project.getProjectLeader()) == 0,
        "您不具备该项目招募权限,请联系管理员");
    //判断岗位是否招满
    final PostInfo postInfo = project.getPostsInfoList().stream()
        .filter(v -> v.getPostCode().equals(laborHire.getPostCode())
            && v.getLevelCode().equals(laborHire.getPostLevelCode())).findFirst().orElse(null);
    if (Objects.isNull(postInfo) || YesNoEnum.NO.eqCode(postInfo.getIsHiring())) {
      throw new RuntimeException("岗位已招满");
    }
    if (postInfo.getPersonDay() - postInfo.getHiredPersonDay() < laborHire.getHireDays()) {
      throw new RuntimeException("岗位需求天数已经不足");
    }
    //组装录用信息
    ProjectLaborEntity laborEntity = buildEntity(laborHire);
    transactionTemplate.executeWithoutResult(transactionStatus -> {
      projectLaborService.saveOrUpdate(laborEntity);
      //处理员工档期
      userScheduleService.saveSchedule(laborEntity, LocalDateUtils.maxDateTime(laborEntity.getHiredTime(),project.getProjectStart()));
      //处理岗位信息
      refreshCostInfo(project);
      messageBusiness.addMessage(MessageTemplateEnum.A20,
          messageBusiness.buildProjectProperties(project,
              new PropertiesExpend("postName", laborHire.getPostName())),
          project.getId(),
          laborEntity.getLaborId(), companyService.getNameById(project.getComId()),
          project.getProjectLeader());
    });

  }

  public void fire(ProjectLaborFire laborFire) {
    final ProjectLaborEntity laborEntity = CommonUtils.requireNotNull(
        projectLaborService.getById(laborFire.getId()), "主键错误");
    final ProjectEntity project = CommonUtils.requireNotNull(
        projectService.getById(laborEntity.getProjectId()), "数据错误,请联系管理员");
    laborEntity
        .setStatus(HireStatusEnum.HIRE_LEAVE.getCode())
        .setRealDays(laborFire.getRealDays())
        .setUpdateBy(UserUtil.getUserId())
        .setUpdateTime(LocalDateTime.now());
    transactionTemplate.executeWithoutResult(transactionStatus -> {
      projectLaborService.updateById(laborEntity);
      //处理岗位信息
      refreshCostInfo(project);
      messageBusiness.addMessage(MessageTemplateEnum.A21,
          messageBusiness.buildProjectProperties(project), project.getId(),
          laborEntity.getLaborId(), companyService.getNameById(project.getComId()),
          project.getProjectLeader());
    });
  }

  private void refreshCostInfo(ProjectEntity project) {
    final Map<String, List<ProjectLaborEntity>> postGroup = Optional.ofNullable(
            projectLaborService.getHiredByProjectId(
                project.getId())).orElseGet(ArrayList::new)
        .stream().collect(Collectors.groupingBy(ProjectLaborEntity::postAndLevelKey));
    final List<PostInfo> postsInfoList = project.getPostsInfoList();
    postsInfoList.forEach(v -> {
      if (postGroup.containsKey(v.groupByPostAndLevel())) {
        final List<ProjectLaborEntity> projectLaborEntities = postGroup.get(
            v.groupByPostAndLevel());
        final int personDays = projectLaborEntities.stream()
            .mapToInt(ProjectLaborEntity::getRealDays)
            .sum();
        v.setHiredNumber(projectLaborEntities.size())
            .setHiredPersonDay(personDays)
            .setHiredSalaryTotal(v.getSalaryByDay().multiply(BigDecimal.valueOf(personDays)))
            //人天数或者人数满足则招满
            .updateHireStatus()
        ;
      }
    });
    project.setPostsInfoList(postsInfoList);
    projectService.updateById(project);
  }

  private ProjectLaborEntity buildEntity(ProjectLaborHire laborHire) {
    final UserInfoModel loginUser = UserUtil.getLoginUser();
    ProjectLaborEntity laborEntity = new ProjectLaborEntity();
    final Long id = laborHire.getId();
    if (Objects.nonNull(id)) {
      laborEntity = CommonUtils.requireNotNull(projectLaborService.getById(id),
          "该用户申请进组信息不存在");
    }
    final Long userId = loginUser.getUserId();
    final LocalDateTime now = LocalDateTime.now();
    //补充岗位级别和天数,审核员信息
    laborEntity.setProjectId(
            CommonUtils.firstNotBlank(laborEntity.getProjectId(), laborHire.getProjectId()))
        .setStatus(HireStatusEnum.HIRE_PASS.getCode())
        .setPostCode(CommonUtils.firstNotBlank(laborEntity.getPostCode(), laborHire.getPostCode()))
        .setPostName(CommonUtils.firstNotBlank(laborEntity.getPostName(), laborHire.getPostName()))
        .setPostLevelCode(laborHire.getPostLevelCode())
        .setPostLevelName(laborHire.getPostLevelName())
        .setHireDays(laborHire.getHireDays())
        .setRealDays(laborHire.getHireDays())
        .setHiredBy(CommonUtils.firstNotNull(laborEntity.getCreateBy(), userId))
        .setHiredTime(CommonUtils.firstNotNull(laborEntity.getCreateTime(), now))
        .setCreateBy(userId)
        .setCreateTime(now)
        .setUpdateBy(userId)
        .setUpdateTime(now)
    ;
    //如果是岗位录用需要额外补充用户信息
    if (Objects.isNull(id)) {
      final UserEntity user = CommonUtils.requireNotNull(
          userService.getById(laborHire.getLaborId()), "被录用用户不存在");
      if (YesNoEnum.isTrue(user.getBanFlag())) {
        throw new RuntimeException("该用户被封号,不能被录用");
      }
      laborEntity.setLaborId(laborHire.getLaborId())
          .setLaborName(user.getName())
          .setLaborPhone(user.getPhone());
    }
    return laborEntity;
  }

  private CommonPageInfo<ProjectLaborInfo> buildResult(
      CommonPageInfo<ProjectLaborEntity> pageInfo, String projectId) {
    final CommonPageInfo<ProjectLaborInfo> results = pageInfo.convert(
        ProjectLaborConverter.INSTANCE::entity2Info);
    if (CollUtil.isNotEmpty(results.getList())) {
      final ProjectEntity project = CommonUtils.requireNotNull(projectService.getById(projectId),
          "项目不存在");
      final List<Long> userIds = results.getList().stream().map(ProjectLaborInfo::getLaborId)
          .collect(Collectors.toList());
      final Map<Long, UserEntity> userMap = userService.userMapByIds(userIds);
      results.getList().forEach(item -> {
        item.setLaborAvatar(userMap.get(item.getLaborId()).getAvatar())
            .setProjectStart(project.getProjectStart())
            .setProjectEnd(project.getProjectEnd());
      });
    }
    return results;
  }


  public void finishWork(String projectId) {
    //执行中,并且未打卡
    final ProjectEntity projectEntity = CommonUtils.requireNotNull(
        projectService.getById(projectId), "项目ID不合法");
    CommonUtils.assertsTrueWithMessage(
        ProjectStatusEnum.ON_EXECUTION.eqCode(projectEntity.getStatus()), "项目不满足打卡状态");
    final List<ProjectLaborEntity> workingLabor = CommonUtils.requireNotNull(
        projectLaborService.getWorkingLaborByProjectId(projectId), "项目下无在岗人员");
    workingLabor.stream().filter(v -> UserUtil.getUserId().equals(v.getLaborId()))
        .forEach(v -> {
              v.setWorkFinishFlag(YesNoEnum.YES.getCode())
                  .setUpdateBy(UserUtil.getUserId())
                  .setUpdateTime(LocalDateTime.now());
            }
        );
    final boolean allFinished = workingLabor.stream()
        .allMatch(v -> YesNoEnum.YES.eqCode(v.getWorkFinishFlag()));
    transactionTemplate.executeWithoutResult(transactionStatus -> {
      projectLaborService.updateBatchById(workingLabor);
      if (allFinished) {
        projectEntity.setStatus(ProjectStatusEnum.ON_DELIVERY.getCode())
            .setUpdateBy(UserUtil.getUserId())
            .setUpdateTime(LocalDateTime.now());
        projectService.updateById(projectEntity);
        //
        messageBusiness.addMessage(MessageTemplateEnum.A3,
            messageBusiness.buildProjectProperties(projectEntity), projectEntity.getId(),
            projectEntity.getProjectLeader(),
            companyService.getNameById(projectEntity.getComId()), projectEntity.getCreateBy());
      }
    });
  }

  public List<Long> listHiredUserByProjectId(String projectId) {
    return listHiredByProjectId(projectId).stream().map(ProjectLaborEntity::getLaborId)
        .collect(Collectors.toList());
  }

  public List<ProjectLaborEntity> listHiredByProjectId(String projectId) {
    return Optional.ofNullable(projectLaborService.getHiredByProjectId(projectId))
        .orElseGet(ArrayList::new);
  }

  public void checkUserInProjectPost(@NotNull(message = "员工ID不能为空") Long laborId,
      @NotBlank(message = "项目ID不能为空") String projectId,
      @NotBlank(message = "岗位不能为空") String postName) {
    if (projectLaborService.countUserInProjectPost(projectId, laborId, postName) <= 0) {
      throw new RuntimeException("该员工未加入该项目该岗位");
    }
  }

  public ProjectLaborEntity getById(@NotNull(message = "溯源ID不能为空") Long traceId) {
    return CommonUtils.requireNotNull(projectLaborService.getById(traceId), "项目员工Id不合法");
  }

  public Long getCompleteCount() {
    return (long) projectLaborService.getCompleteProject(UserUtil.getUserId()).size();
  }

  public Long getApplyCount() {
    return (long) projectLaborService.getApplyProject(UserUtil.getUserId()).size();
  }

  public Long getExecutionCountByUser() {
    return (long) projectLaborService.getExecutionCount(UserUtil.getUserId()).size();
  }
}
